package com.maxrave.simpmusic.service

import android.app.PendingIntent
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.os.Binder
import android.os.IBinder
import android.util.Log
import androidx.media3.common.Player
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.session.DefaultMediaNotificationProvider
import androidx.media3.session.MediaController
import androidx.media3.session.MediaLibraryService
import androidx.media3.session.MediaSession
import androidx.media3.session.SessionToken
import com.google.common.util.concurrent.MoreExecutors
import com.maxrave.simpmusic.R
import com.maxrave.simpmusic.common.Config.SERVICE_SCOPE
import com.maxrave.simpmusic.common.MEDIA_NOTIFICATION
import com.maxrave.simpmusic.service.test.CoilBitmapLoader
import com.maxrave.simpmusic.ui.MainActivity
import com.maxrave.simpmusic.ui.widget.BasicWidget
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.cancel
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import org.koin.core.qualifier.named

@UnstableApi
class SimpleMediaService : MediaLibraryService(), KoinComponent {
//  通过  依赖注入自动实例化对象并赋值
    private val player: ExoPlayer by inject()
    private val coilBitmapLoader: CoilBitmapLoader by inject()
    private val serviceCoroutineScope: CoroutineScope by inject(named(SERVICE_SCOPE))

    private lateinit var mediaSession: MediaLibrarySession

    private val simpleMediaSessionCallback: SimpleMediaSessionCallback by inject()

    private val simpleMediaServiceHandler: SimpleMediaServiceHandler by inject()

    private val binder = MusicBinder()

    @UnstableApi
    override fun onCreate() {
        super.onCreate()
        Log.w("Service", "Simple Media Service Created")
        setMediaNotificationProvider(
            DefaultMediaNotificationProvider(
                this,
                { MEDIA_NOTIFICATION.NOTIFICATION_ID },
                MEDIA_NOTIFICATION.NOTIFICATION_CHANNEL_ID,
                R.string.notification_channel_name,
            ).apply {
                setSmallIcon(R.drawable.mono)
            },
        )

        mediaSession =
            provideMediaLibrarySession(
                this,
                this,
                player,
                simpleMediaSessionCallback
            )

       //  Set late init 设置通知布局
        simpleMediaServiceHandler.setNotificationLayout = { listCommand ->
            mediaSession.setCustomLayout(listCommand)
        }
        val sessionToken = SessionToken(this, ComponentName(this, SimpleMediaService::class.java))
        val controllerFuture = MediaController.Builder(this, sessionToken).buildAsync()
        controllerFuture.addListener({ controllerFuture.get() }, MoreExecutors.directExecutor())
    }

    @UnstableApi
    override fun onStartCommand(
        intent: Intent?,
        flags: Int,
        startId: Int,
    ): Int {
        if (intent != null && intent.action != null) {
            when (intent.action) {
                BasicWidget.ACTION_TOGGLE_PAUSE -> {
                    if (player.isPlaying) player.pause() else player.play()
                }

                BasicWidget.ACTION_SKIP -> {
                    player.seekToNext()
                }

                BasicWidget.ACTION_REWIND -> {
                    player.seekToPrevious()
                }
            }
        }
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaLibrarySession = mediaSession

    @UnstableApi
    override fun onUpdateNotification(
        session: MediaSession,
        startInForegroundRequired: Boolean,
    ) {
        super.onUpdateNotification(session, startInForegroundRequired)
    }

    @UnstableApi
    private fun release() {
        mediaSession.run {
            release()
            if (player.playbackState != Player.STATE_IDLE) {
                player.release()
            }
        }
    }

    @UnstableApi
    override fun onDestroy() {
        super.onDestroy()
        serviceCoroutineScope.cancel()
        release()
    }
    // 内存管理
    override fun onTrimMemory(level: Int) {
        super.onTrimMemory(level)
        simpleMediaServiceHandler.mayBeSaveRecentSong()
    }


    /** 功能：此方法在相关任务被移除时被调用，通常是当用户从多任务视图中关闭应用时。
    参数：rootIntent是启动该任务的意图。
    实现：调用父类的同名方法，以保持标准行为。当前没有额外的逻辑，因此onTaskRemoved不会影响其他功能。  */



    @UnstableApi
    override fun onTaskRemoved(rootIntent: Intent?) {
        super.onTaskRemoved(rootIntent)
    }
    /**功能：MusicBinder是一个内部类，继承自Binder。它用于连接到SimpleMediaService。
    服务引用：通过get()方法，返回SimpleMediaService的实例，允许外部组件与该服务进行交互。  */
    inner class MusicBinder : Binder() {
        val service: SimpleMediaService
            get() = this@SimpleMediaService
    }
  /** 功能：创建并返回一个MediaLibrarySession实例。
  参数：
  context：应用的上下文。
  service：MediaLibraryService的实例。
  player：ExoPlayer实例，用于媒体播放。
  callback：用于处理会话回调的SimpleMediaSessionCallback。
  实现：
  使用MediaLibrarySession.Builder构建会话。
  设置会话活动，即点击通知后打开的活动（MainActivity）。
  设置位图加载器（coilBitmapLoader）用于加载图像。
  返回构建的MediaLibrarySession对象。 */
    override fun onBind(intent: Intent?): IBinder = super.onBind(intent) ?: binder

    // Can't inject by Koin because it depend on service
    @UnstableApi
    private fun provideMediaLibrarySession(
        context: Context,
        service: MediaLibraryService,
        player: ExoPlayer,
        callback: SimpleMediaSessionCallback
    ): MediaLibrarySession =
        MediaLibrarySession
            .Builder(
                service,
                player,
                callback,
            ).setSessionActivity(
                PendingIntent.getActivity(
                    context,
                    0,
                    // 为特定组件创建 intent。所有其他字段（action、data、type、class）均为 null，
                    // 但稍后可以通过显式调用对其进行修改。这提供了一种便捷的方法来创建旨在执行硬编码类名的 intent，而不是依赖系统为您找到合适的类;
                    Intent(context, MainActivity::class.java),
                    PendingIntent.FLAG_IMMUTABLE,
                ),
            ).setBitmapLoader(coilBitmapLoader)
            .build()
}
